import * as THREE from 'three';
import M from 'scripts/main.js';

import Stats from 'three/addons/libs/stats.module.js';
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
//鼠标控制镜头
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { Capsule } from 'three/addons/math/Capsule.js';
import { Octree } from 'three/addons/math/Octree.js';
import { TransformControls } from 'three/addons/controls/TransformControls.js';
import {Shop} from 'scripts/shop.js';
import {School} from 'scripts/school.js';
import {Square} from 'scripts/square.js';
import {Market} from 'scripts/market.js';
			
class City {
	constructor(){	
		let scene, renderer, camera, stats;
		let model, skeleton, mixer, clock, orbit, tcontrol,panel;
		const raycaster = new THREE.Raycaster();
		const m = new M();
		THREE.Cache.enabled = true;

		let currentBaseAction = 'idle';
		const allActions = [];
		const baseActions = {
			idle: { weight: 1 },
			walk: { weight: 0 },
			run: { weight: 0 }
		};
		let numAnimations,animationID;

		
		//用户的碰撞检测体
		let playerCollider;
		//玩家的位置
		let playerVelocity = new THREE.Vector3();
		//玩家的方向
		let playerDirection = new THREE.Vector3();

		let cameraCollider;
		//相机的位置
		let cameraVelocity = new THREE.Vector3();
		//相机的方向
		let cameraDirection = new THREE.Vector3();

		const GRAVITY = 50;

		let isEditMode = false;
		let transferMode = false;
		let playerOnFloor = true;
		let objects = [];
		let city=[
			{name:"道路",position:{x:0.2,z:3.05}},
			{name:"城市",position:{x:0,z:0},action:'goOut'},
			{name:"城主府",position:{x:6.15,z:1.9},rotation:{y:180},action:'home'},
			{name:"演武场",position:{x:7.6,z:-9.6},action:'square'},
			{name:"市场",position:{x:-12.5,z:-5.5},action:'market'},
			{name:"书院",position:{x:6.45,z:11.5},rotation:{y:90},action:'school'},
			{name:"大饭店",position:{x:-6.1,z:2.35},rotation:{y:180},action:'restaurant'},
			{name:"商店",position:{x:2.77,z:-3.95},rotation:{y:90},action:'shop'},
			{name:"井",position:{x:1.9,z:0.45}},
			{name:"小树",position:{x:2.9,z:0.6}},
			{name:"大树",position:{x:1.7,z:1.45}},
		];
		let nfts=[];
		let settings = {
			'开启':false,
			'模式':'拖动',
			'网格锁定':false,
			'角度锁定':true,
			'倍数锁定':false,
			'取消': function () {
				if (selectedObject){
					setLoaction(selectedObject,selectedObject.tag);
					selectedObject = null;
				}
			},
			'收起': stow,
			'功能转移': transfer,
			'保存': saveParam,
		}
		let modes=["拖动","旋转","缩放"];
		const keyStates = {};
		let mouse, selectedObject;	
		init();	
		
		async function init() {
			m.display("goback",false);
			document.addEventListener( 'keydown', keydown );
			document.addEventListener( 'keyup', keyup );
			document.addEventListener( 'mousedown', mousedown);
			document.addEventListener( 'mouseup', mouseup );
			window.addEventListener( 'resize', onWindowResize );

			let loadData = m.loadJson("city");
			if (loadData) city = loadData;

			loadData = m.loadJson("nfts");
			if (loadData) nfts = loadData;

			let money = m.loadInt("money");
			if (!money) money = 100000;
			m.setMoney(money);
			
			let honor = m.loadInt("honor");
			if (!honor) honor = 0;
			m.setHonor(honor);
			
			//TODO:能量的恢复
			let energy = m.loadInt("energy");
			if (!energy) energy = 1000;
			m.setEnergy(energy);

			const container = m.getNode( 'container' );
			clock = new THREE.Clock();

			scene = new THREE.Scene();
			scene.background = new THREE.Color( 0xa0a0a0 );
			scene.fog = new THREE.Fog( 0xa0a0a0, 10, 50 );

			const hemiLight = new THREE.HemisphereLight( 0xffffff, 0x444444 );
			hemiLight.position.set( 0, 20, 0 );
			scene.add( hemiLight );

			// ground

			renderer = new THREE.WebGLRenderer( { antialias: true } );
			renderer.setPixelRatio( window.devicePixelRatio );
			renderer.setSize( window.innerWidth, window.innerHeight );
			renderer.outputEncoding = THREE.sRGBEncoding;
			//renderer.shadowMap.enabled = true;
			container.appendChild( renderer.domElement );

			// camera
			camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, 100 );
			orbit = new OrbitControls( camera, renderer.domElement );

			for(var i=0;i<city.length;i++){
				await loadWorld(city[i]);
			}
			await loadModel();

			camera.position.set( model.position.x-0.5, model.position.y+1.2, model.position.z-4 );
			cameraCollider = new Capsule( new THREE.Vector3( 0, 0, 0 ), camera.position, 0.1 );

			orbit.enablePan = true;
			orbit.enableZoom = true;
			orbit.maxPolarAngle = 1.6;
			orbit.minPolarAngle = 0;
			orbit.target.set( model.position.x, model.position.y+0.3, model.position.z );
			orbit.update();
			const loader = new THREE.TextureLoader();
			const bgTexture = loader.load(m.current()+'images/sky.jpg');
			scene.background = bgTexture;
			tcontrol = new TransformControls( camera, renderer.domElement );
			tcontrol.setRotationSnap( THREE.MathUtils.degToRad( 45 ));

			panel = new GUI({width:150,title:'编辑模式'});
			panel.add( settings, '开启' ).onChange( editMode );
			const modeChange = panel.add( settings, '模式' ).options( modes );
			modeChange.onChange( function () {
				if(settings.模式=="拖动"){
					tcontrol.setMode( 'translate' );
				}else if(settings.模式=="旋转"){
					tcontrol.setMode( 'rotate' );
				}else if(settings.模式=="缩放"){
					tcontrol.setMode( 'scale' );
				}
			} );			
			panel.add( settings, '网格锁定' ).onChange( mashLock );
			panel.add( settings, '角度锁定' ).onChange( angleLock );
			panel.add( settings, '倍数锁定' ).onChange( sizeLock );
			panel.add( settings, '取消' );
			panel.add( settings, '收起' );
			panel.add( settings, '功能转移' );
			panel.add( settings, '保存' );
			const folder1 = panel.addFolder( '放置' );
			for(let i=0;i<nfts.length;i++){
				let name = nfts[i].name;
				let tmp = {};
				tmp[name] = function(){
					place(name);
				};
				folder1.add(tmp,name);
			}
			panel.close();
			
			animate();

		}
		//载入环境
		async function loadWorld(obj){
			const p = await new Promise((resolve)=>{				
				const loader = new GLTFLoader();
				//载入环境
				loader.load( m.current() + 'models/'+obj.name+'.glb', ( gltf ) => {
					
					//场景中添加环境
					scene.add( gltf.scene );
					gltf.scene.tag = obj;
					setLoaction(gltf.scene, obj);
					//添加环境中的碰撞体
					//worldOctree.fromGraphNode( gltf.scene );
					gltf.scene.traverse( child => {
						//为子对象设置阴影
						if ( child.isMesh ) {
							objects.push(child);
							//child.castShadow = true;
							//child.receiveShadow = true;
						}
					} );
					
					resolve(gltf.scene);
				} );
		  });
		  return p;
		}
		//载入人物
		async function loadModel(){
			const p = await new Promise(async (resolve)=>{				
			const loader = new GLTFLoader();
				loader.load( m.current()+'models/Xbot.glb', function ( gltf ) {

					model = gltf.scene;
					scene.add( model );
					model.position.set( 0,0.02,0 );
					model.scale.set( 0.2,0.2,0.2 );
					model.castShadow = true;
					playerCollider = new Capsule( new THREE.Vector3( 0.1, 0.3, 0.1 ), model.position, 0.35 );

					model.traverse( function ( object ) {

						if ( object.isMesh ) object.castShadow = true;

					} );

					skeleton = new THREE.SkeletonHelper( model );
					skeleton.visible = false;
					scene.add( skeleton );

					const animations = gltf.animations;
					mixer = new THREE.AnimationMixer( model );

					numAnimations = animations.length;

					for ( let i = 0; i !== numAnimations; ++ i ) {

						let clip = animations[ i ];
						const name = clip.name;

						if ( baseActions[ name ] ) {

							const action = mixer.clipAction( clip );
							activateAction( action );
							baseActions[ name ].action = action;
							allActions.push( action );
						}

					}
					numAnimations = allActions.length;
					resolve(true);
				} );
		  });
		  return p;
		}
		//保存建筑设定
		function saveParam(){
			if (selectedObject){
				let obj = selectedObject;
				obj.tag.position.x = obj.position.x;
				obj.tag.position.y = obj.position.y;
				obj.tag.position.z = obj.position.z;

				obj.tag.rotation.x = obj.rotation.x * 180 / Math.PI;
				obj.tag.rotation.y = obj.rotation.y * 180 / Math.PI;
				obj.tag.rotation.z = obj.rotation.z * 180 / Math.PI;
				
				obj.tag.scale.x = obj.scale.x;
				obj.tag.scale.y = obj.scale.y;
				obj.tag.scale.z = obj.scale.z;
				m.saveItem("city",JSON.stringify(city));
			}
		}
		//根据设定载入建筑
		function setLoaction(obj,param){
			if (!param.position) param.position={};
			if (!param.position.x) param.position.x=0;
			if (!param.position.y) param.position.y=0;
			if (!param.position.z) param.position.z=0;
			obj.position.x = param.position.x;
			obj.position.y = param.position.y;
			obj.position.z = param.position.z;

			if (!param.rotation) param.rotation={};
			if (!param.rotation.x) param.rotation.x=0;
			if (!param.rotation.y) param.rotation.y=0;
			if (!param.rotation.z) param.rotation.z=0;
			obj.rotation.x = param.rotation.x*Math.PI/180;
			obj.rotation.y = param.rotation.y*Math.PI/180;
			obj.rotation.z = param.rotation.z*Math.PI/180;

			if (!param.scale) param.scale={}; 
			if (!param.scale.x) param.scale.x=1; 
			if (!param.scale.y) param.scale.y=1;
			if (!param.scale.z) param.scale.z=1;
			obj.scale.x = param.scale.x;
			obj.scale.y = param.scale.y;
			obj.scale.z = param.scale.z;
		}
		//放置建筑物
		async function place(name){
			if (isEditMode){
				let building = {name:name,position:model.position};
				city.push(building);
				selectedObject = await loadWorld(building);
				tcontrol.attach( selectedObject );
				for(let i=0;i<nfts.length;i++){
					if (nfts[i].name == name){
						nfts.splice(i,1);
						break;
					}
				}
				for(let i=0;i<panel.folders[0].children.length;i++){
					if (panel.folders[0].children[i].property == selectedObject.tag.name){
						panel.folders[0].children[i].destroy();
						break;
					}
				}
				saveParam();
				m.saveItem("nfts",JSON.stringify(nfts));
			}else{
				alert("请先开启编辑模式");
			}
		}

		//收起建筑物
		function stow(){
			if (isEditMode && selectedObject){
				if(selectedObject.tag.action){
					alert("您选中的建筑有专属的功能，请先进行功能转移");
					return;
				} 
				if (confirm("您要将选中的建筑收起来吗？")){
					tcontrol.detach();
					deleteGroup(selectedObject);
					scene.remove(selectedObject);
					nfts.push({name:selectedObject.tag.name});
					for(let i=0;i<city.length;i++){
						if (city[i] == selectedObject.tag){
							city.splice(i,1);
							break;
						}
					}
					
					let name = selectedObject.tag.name;
					let tmp = {};
					tmp[name] = function(){
						place(name);
					};
					panel.folders[0].add(tmp,name);
					selectedObject = null;
					m.saveItem("city",JSON.stringify(city));
					m.saveItem("nfts",JSON.stringify(nfts));
				}
			}else{
				alert("请先开启编辑模式，选择要收起的建筑");
			}
		}
		
		//转移建筑物功能
		function transfer(){
			if (isEditMode && selectedObject){
				if(!selectedObject.tag.action){
					alert("您选中的建筑没有专属的功能");
					return;
				}
				transferMode = true;
				alert("请选择要转移的建筑");
			}else{
				alert("请先开启编辑模式，选择要功能转移的建筑");
			}
		}

		//只在载入动画时调用一次，目的似乎是为了将其激活
		function activateAction( action ) {

			const clip = action.getClip();
			const settings = baseActions[ clip.name ];
			setWeight( action, settings.weight );
			action.play();

		}
		
		//动作切换
		function prepareCrossFade( start, end, duration ) {
			if (end == currentBaseAction) return;
			const startAction = baseActions[ start ].action;
			const endAction = baseActions[ end ].action;

			if ( endAction ) {

				executeCrossFade( startAction, endAction, duration );

			} else {

				synchronizeCrossFade( startAction, endAction, duration );

			}
			currentBaseAction = endAction.getClip().name;
		}
		//等待前一个动作结束，再开始新动作
		function synchronizeCrossFade( startAction, endAction, duration ) {

			mixer.addEventListener( 'loop', onLoopFinished );

			function onLoopFinished( event ) {

				if ( event.action === startAction ) {

					mixer.removeEventListener( 'loop', onLoopFinished );

					executeCrossFade( startAction, endAction, duration );

				}

			}

		}
		//开始新动作
		function executeCrossFade( startAction, endAction, duration ) {

			// Not only the start action, but also the end action must get a weight of 1 before fading
			// (concerning the start action this is already guaranteed in this place)

			if ( endAction ) {

				setWeight( endAction, 1 );
				endAction.time = 0;

				if ( startAction ) {

					// Crossfade with warping

					startAction.crossFadeTo( endAction, duration, true );

				} else {

					// Fade in

					endAction.fadeIn( duration );

				}

			} else {

				// Fade out

				startAction.fadeOut( duration );

			}

		}

		// This function is needed, since animationAction.crossFadeTo() disables its start action and sets
		// the start action's timeScale to ((start animation's duration) / (end animation's duration))
		function setWeight( action, weight ) {

			action.enabled = true;
			action.setEffectiveTimeScale( 1 );
			action.setEffectiveWeight( weight );

		}
		//键盘操作
		function keyControls( deltaTime ) {

			// 移动速度
			const speedDelta = deltaTime * 24;

			if ( keyStates[ 'KeyW' ] ) {
				var v = getPlayerdVector().multiplyScalar( speedDelta ) ;
				playerVelocity.add(v );
				cameraVelocity.add(v );

			}

			if ( keyStates[ 'KeyS' ] ) {
				var v = getPlayerdVector().multiplyScalar( - speedDelta ) ;

				playerVelocity.add( v );
				cameraVelocity.add( v );

			}

			if ( keyStates[ 'KeyA' ] ) {
				model.rotation.y += 0.05;
			}

			if ( keyStates[ 'KeyD' ] ) {

				model.rotation.y -= 0.05;

			}

		}
		//前后移动
		function getPlayerdVector() {

			model.getWorldDirection( playerDirection );
			playerDirection.y = 0;
			playerDirection.normalize();
			return playerDirection;

		}
		//玩家碰撞检测
		function playerCollisions() {
			/*
			const result = worldOctree.capsuleIntersect( playerCollider );

			playerOnFloor = false;

			if ( result ) {

				playerOnFloor = result.normal.y > 0;

				if ( ! playerOnFloor ) {

					playerVelocity.addScaledVector( result.normal, - result.normal.dot( playerVelocity ) );

				}

				playerCollider.translate( result.normal.multiplyScalar( result.depth ) );

			}
			*/
		}

		//刷新玩家状态
		function updatePlayer( deltaTime ) {

			let damping = Math.exp( - 24 * deltaTime ) - 1;

			if ( ! playerOnFloor ) {
				playerVelocity.y -= GRAVITY * deltaTime;
				// small air resistance
				damping *= 0.1;
			}

			//计算受到阻力后的位置
			playerVelocity.addScaledVector( playerVelocity, damping );
			cameraVelocity.addScaledVector( cameraVelocity, damping );
			//得到经过时间片段后的新位置
			const deltaPosition = playerVelocity.clone().multiplyScalar( deltaTime );
			const cameraPosition = cameraVelocity.clone().multiplyScalar( deltaTime );
			//将人和相机都移动到新位置
			playerCollider.translate( deltaPosition );
			cameraCollider.translate( cameraPosition );
			//碰撞检测
			playerCollisions();
			//调整镜头对准的位置
			orbit.target.set( model.position.x, model.position.y+0.3, model.position.z );

		}		
		function onWindowResize() {

			camera.aspect = window.innerWidth / window.innerHeight;
			camera.updateProjectionMatrix();

			renderer.setSize( window.innerWidth, window.innerHeight );

		}
		function animate() {

			// Render loop

			animationID = requestAnimationFrame( animate );

			// Get the time elapsed since the last frame, used for mixer update

			const mixerUpdateDelta = clock.getDelta();

			// Update the animation mixer, and render this frame

			mixer.update( mixerUpdateDelta );
			keyControls( mixerUpdateDelta );
			updatePlayer( mixerUpdateDelta );
			orbit.update();
			render();

		}
		function keyup(event){
			if (event.code=="KeyW" || event.code=="KeyS")
				prepareCrossFade( "run", "idle", 0.35 );
			keyStates[ event.code ] = false;
		}
		function keydown(event){
			if (event.code=="KeyW" || event.code=="KeyS")
				prepareCrossFade( "idle", "run", 0.35 );
			keyStates[ event.code ] = true;
		}
		function mousedown(event){
			mouse = event;
			mouse.time = new Date().getTime(); 
		}
		function mouseup(event){
			if (mouse.clientX==event.clientX 
				&& mouse.clientY==event.clientY 
				&& mouse.time+200 > new Date().getTime()){
				//屏幕范围
				const rect = renderer.domElement.getBoundingClientRect();
				//鼠标点击位置
				const pointer = new THREE.Vector2();
				//所有交叉的点
				const intersections = [];
				//计算屏幕坐标
				pointer.x = ( event.clientX - rect.left ) / rect.width * 2 - 1;
				pointer.y = - ( event.clientY - rect.top ) / rect.height * 2 + 1;
				//设置当前视角和点击位置
				raycaster.setFromCamera( pointer, camera );
				//获取这条射线上所有交叉的点
				raycaster.intersectObjects( objects, true, intersections );
				if ( intersections.length > 0 ) {
					//根据点所在的对象获取建筑物
					var obj = getBuilding(intersections[ 0 ].object);
					if (isEditMode){
						//城市和道路不可编辑
						if (obj.tag.name!="城市" && obj.tag.name!="道路"){
							if (transferMode){
								if (obj.tag.action){
									alert("选中的建筑已经有专属功能了。");
								}else{
									obj.tag.action = selectedObject.tag.action;
									selectedObject.tag.action = null;
									selectedObject = obj;
									//将编辑空间绑定在选定建筑上
									tcontrol.attach( selectedObject );
									saveParam();
									alert("转移成功");
								}
							}else{
								selectedObject = obj;
								//将编辑空间绑定在选定建筑上
								tcontrol.attach( selectedObject );
							}
						}
					}else{
						//如果建筑有绑定的方法，则执行
						if(obj.tag.action) eval(obj.tag.action+"()");
					}
				}
				//内置方法，递归获取建筑
				function getBuilding(obj){
					if (obj.tag!=null) return obj;
					else {
						return getBuilding(obj.parent);
					}
				};
			}				
		}
		//渲染
		function render() {
			renderer.render( scene, camera );
		}
		//开关编辑模式
		function editMode(){
			isEditMode = !isEditMode;
			if (isEditMode){
				//添加对象调整控件
				scene.add( tcontrol );
				//发生调整时重新渲染
				tcontrol.addEventListener( 'change', render );
				//调整时屏蔽第三人称控件
				tcontrol.addEventListener( 'dragging-changed', function ( event ) {
					orbit.enabled = ! event.value;
				} );
			}else{
				transferMode = false;
				//删除对象调整控件
				saveParam();
				scene.remove(tcontrol);
				tcontrol.removeEventListener( 'change', render );
			}
		}
		//开关距离锁定
		function mashLock(){
			if(settings.网格锁定)	tcontrol.setTranslationSnap( 1 );
			else tcontrol.setTranslationSnap( null );
		}
		//开关角度锁定
		function angleLock(){
			if(settings.角度锁定)	tcontrol.setRotationSnap( THREE.MathUtils.degToRad( 45 ) );
			else tcontrol.setRotationSnap(null);
		}
		//开关倍数锁定
		function sizeLock(){
			if(settings.倍数锁定)	tcontrol.setScaleSnap( 0.25 );
			else tcontrol.setScaleSnap(null);
		}				
		function home(){
			if (confirm('要回到城主府吗？')){
				
			}
		}
		function goOut(){
			if (confirm('要去其他玩家的城市吗？')){
				alert("此功能正在开发中");
				
			}
		}
		function square(){
			if (confirm('要进入演武场吗？')){
				let books = m.loadJson("books");
				if (books) {
					books.sort((a,b)=>{
						return a.id-b.id;
					});
					destroy();
					new Square(books);
				}else{
					alert("您还没有课本，请先去商店购买。");
				}
				m.saveJson("books",books);
			}
		}
		function market(){
			if (confirm('要进入市场吗？')){
				destroy();
				new Market();
			}
		}
		function school(){
			if (confirm('要进入书院吗？')){
				let books = m.loadJson("books");
				if (books) {
					books.sort((a,b)=>{
						return a.id-b.id;
					});
					destroy();
					new School(books);
				}else{
					alert("您还没有课本，请先去商店购买。");
				}
				m.saveJson("books",books);
			}
		}
		function restaurant(){
			if (confirm('要进入饭店吗？')){
				
			}
		}
		function shop(){
			if (confirm('要进入商店吗？')){
				destroy();
				new Shop();
			}
		}
		/**
		* 清除模型，模型中有 group 和 scene,需要进行判断
		* @param scene
		* @returns
		*/
		function clearScene(){
			// 从scene中删除模型并释放内存
			if(objects.length > 0){		
				for(var i = 0; i< objects.length; i++){
					var currObj = objects[i];
					
					// 判断类型
					if(currObj instanceof THREE.Scene){
						var children = currObj.children;
						for(var i = 0; i< children.length; i++){
							deleteGroup(children[i]);
						}	
					}else{				
						deleteGroup(currObj);
					}
					scene.remove(currObj);
				}
			}
		}

		// 删除group，释放内存
		function deleteGroup(group) {
	    if (!group) return;
	    // 删除掉所有的模型组内的mesh
	    group.traverse(function (item) {
	        if (item instanceof THREE.Mesh) {
	            item.geometry.dispose(); // 删除几何体
	            item.material.dispose(); // 删除材质
	        }
	    });
		}

		
		function destroy(){
			cancelAnimationFrame(animationID)
			panel.destroy();
			clearScene();
			scene.clear();
			scene.background = null;
			renderer.dispose();
			let gl = renderer.domElement.getContext("webgl");
			gl && gl.getExtension("WEBGL_lose_context").loseContext();
			renderer.forceContextLoss();
			renderer.content = null;

			document.removeEventListener( 'keydown', keydown );
			document.removeEventListener( 'keyup', keyup );
			document.removeEventListener( 'mousedown', mousedown);
			document.removeEventListener( 'mouseup', mouseup );
			window.removeEventListener( 'resize', onWindowResize );
			m.getNode("container").removeChild(renderer.domElement);
			scene = null;
			orbit = null;
			camera = null;
			tcontrol = null;
			renderer = null;
		}
	}
}
export { City };